/*
 *  Copyright (C) 2010 JS.MinecraftSavior
 *
 *  This file is part of the Minecraft Savior utility.
 *
 *  Minecraft Savior is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Minecraft Savior is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.utility.MinecraftSavior.ui;

import com.utility.MinecraftSavior.file.*;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Toolkit;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.Scanner;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import javax.swing.UIManager;

public class MCS extends javax.swing.JFrame {
    // Begin settings file vars

    private static final String SETTINGS_FILE = "Settings.mcs";
    private static final String SETTINGS_SEPARATOR = "=";
    // End settings file vars
    // Begin list of settings
    private static final String SAVE_DIRECTORY = "SaveDirectory";
    private static final String BACKUPS_DIRECTORY = "BackupsDirectory";
    private static final String LAST_BACKUP = "LastBackup";
    private static final String ASKED_ABOUT_SCRIPTS = "AskedAboutScripts";
    private static final String USE_SCRIPTS = "UseScripts";
    // End list of settings
    // Begin list of Windows VBS/Batch file info
    private static final String SHUTDOWN_SERVER_SCRIPT = "opentest.vbs";
    private static final String START_SERVER_SCRIPT = "open.bat";
    // End list of VBS/Batch file info
    // Begin OS vars
    private String OS;
    private boolean onWindows;
    private boolean onXP;
    private boolean onLinux;
    private boolean onMac;
    private String homeDir;
    // End OS vars
    // Begin backup/restore vars
    private File saveDir;
    private boolean saveDirSelectedB;
    private boolean saveDirSelectedR;
    private File backupDir;
    private boolean backupDirSelectedB;
    private boolean backupDirSelectedR;
    private String worldToBackup;
    private BackupTask backupTask;
    private String restoreZip;
    private String worldToRestore;
    private RestoreTask restoreTask;
    private boolean askedAboutScripts;
    private boolean usingScripts;
    // End backup/restore vars

    // Default constructor - initialize all components
    public MCS() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {}

        initComponents();

        OS = null;
        onWindows = false;
        onXP = false;
        onLinux = false;
        onMac = false;
        homeDir = null;

        saveDir = null;
        saveDirSelectedB = false;
        saveDirSelectedR = false;

        backupDir = null;
        backupDirSelectedB = false;
        backupDirSelectedR = false;

        worldToBackup = null;

        restoreZip = null;
        worldToRestore = null;

        askedAboutScripts = false;
        usingScripts = false;

        fileChooser.setFileHidingEnabled(false);

        backupsComboBox.setModel(new javax.swing.DefaultComboBoxModel());

        saveDirTextB.setBackground(new java.awt.Color(255, 255, 255));
        backupDirTextB.setBackground(new java.awt.Color(255, 255, 255));
        saveDirTextR.setBackground(new java.awt.Color(255, 255, 255));
        backupDirTextR.setBackground(new java.awt.Color(255, 255, 255));
        worldText.setBackground(new java.awt.Color(255, 255, 255));
        dateText.setBackground(new java.awt.Color(255, 255, 255));
        timeText.setBackground(new java.awt.Color(255, 255, 255));

        getOSInfo();
        findMinecraftSaveDir();
        lookupSavedSettings();

        if (onWindows && !askedAboutScripts)
            askToUseScripts();

        if (onWindows && usingScripts)
            windowsShutdownServer();

        setLocationRelativeTo(null);
    }

    // Get basic OS information like home directory and OS
    private void getOSInfo() {
        homeDir = System.getProperty("user.home");
        OS = System.getProperty("os.name");
        onWindows = OS.startsWith("Windows");
        if (onWindows) {
            onXP = OS.contains("XP");
        }
        onLinux = OS.startsWith("Linux");
        onMac = OS.startsWith("Mac");
    }

    // Auto-locate local Minecraft 'saves' directory
    private void findMinecraftSaveDir() {
        if (onXP) {
            saveDir = new File(homeDir + "/Application Data/.minecraft/saves");
        } else if (onWindows) {
            saveDir = new File(homeDir + "/AppData/Roaming/.minecraft/saves");
        } else if (onLinux) {
            saveDir = new File(homeDir + "/.minecraft/saves");
        } else if (onMac) {
            saveDir = new File(homeDir + "/Library/Application Support/minecraft/saves");
        } else {
            return;
        }

        if (saveDir.isDirectory()) {
            if (!savesExist(saveDir.getAbsolutePath())) {
                saveDirTextB.setForeground(Color.RED);
                saveDirTextB.setText("No saves found in Minecraft directory."
                        + " You could still backup a server folder.");

                saveDirTextR.setForeground(Color.BLACK);
                saveDirTextR.setText(saveDir.toString());
                saveDirSelectedR = true;

                return;
            }

            saveDirTextB.setForeground(Color.BLACK);
            saveDirTextB.setText(saveDir.toString());
            saveDirSelectedB = true;

            saveDirTextR.setForeground(Color.BLACK);
            saveDirTextR.setText(saveDir.toString());
            saveDirSelectedR = true;
        } else {
            saveDir = null;
        }
    }

    // Open SETTINGS_FILE to set last backup location and last world backed up
    private void lookupSavedSettings() {
        Scanner settingsScanner = null;

        try {
            settingsScanner = new Scanner(new File(SETTINGS_FILE));

            while (settingsScanner.hasNextLine()) {
                Scanner curLine = new Scanner(settingsScanner.nextLine()).useDelimiter("=");

                String settingName = curLine.next();
                String settingVal = curLine.next();

                applySetting(settingName, settingVal);
            }
            settingsScanner.close();

            if (backupButtonReady()) {
                backupButton.setEnabled(true);
            }

            if (restoreButtonReady()) {
                restoreButton.setEnabled(true);
            }
        } catch (FileNotFoundException ex) {
            writeSettings();
        } finally {
            if (settingsScanner != null) {
                settingsScanner.close();
            }
        }
    }

    private void askToUseScripts() {
        int choice = JOptionPane.showOptionDialog(this, "MCS has detected that "
                + "you are on a Windows machine.\nIf you are planning to backup/"
                + "restore a server using MCS,\nMCS can (hopefully) auto-shutdown "
                + "and restore your server.\nWould you like to enable this feature "
                + "for the next time you\nuse MCS?\n\nYou can also choose to manually "
                + "shut down and start up\nyour server before using MCS.\n\nIf you do "
                + "not have a server just click \"No\".\n\n", "Use of Server Scripts",
                JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, null,
                null);

        if (choice == JOptionPane.YES_OPTION)
            usingScripts = true;
        else if (choice == JOptionPane.NO_OPTION)
            usingScripts = false;

        askedAboutScripts = true;
    }

    // WINDOWS ONLY! Thanks to rzork for the VBS and batch files!
    private void windowsShutdownServer() {

        String TEST_SCRIPT_NAME = "opentest.vbs";
        String TEST_SCRIPT_NAME2 = "test.bat";

        if (isServerDirectory(saveDir.getAbsolutePath())) {
            if (Desktop.isDesktopSupported()) {
                Desktop d = Desktop.getDesktop();

                if (d.isSupported(Desktop.Action.OPEN)) {
                    try {
                        File testScript = new File(TEST_SCRIPT_NAME2);
                        d.open(testScript);
                    } catch (IllegalArgumentException ex) {
                        JOptionPane.showMessageDialog(this, "Script '" + SHUTDOWN_SERVER_SCRIPT
                                + "' not found!\nPlease remember to shut down your "
                                + "server before using MCS.", "Warning!",
                                JOptionPane.WARNING_MESSAGE);
                    } catch (IOException ex) {
                        JOptionPane.showMessageDialog(this, "Your server was not shut down!\n"
                                + "Please close and open MCS to try again.\n"
                                + "If this does not work, you must manually\n"
                                + "stop your server.", "Warning!", JOptionPane.WARNING_MESSAGE);
                    }
                }
            }
        }
    }

    private void windowsStartupServer() {

    }

    // Append status to backupStatusTextArea and move caret to end
    public static void updateBackupStatusTextArea(String status) {
        backupStatusTextArea.append(status + "\n");
        backupStatusTextArea.setCaretPosition(backupStatusTextArea.getDocument().getLength());

    }

    // Append status to restoreStatusTextArea and move caret to end
    public static void updateRestoreStatusTextArea(String status) {
        restoreStatusTextArea.append(status + "\n");
        restoreStatusTextArea.setCaretPosition(restoreStatusTextArea.getDocument().getLength());
    }

    // Return string version of which world is selected on the Backup tab
    private String getSelectedWorld() {
        if (world1RadioButton.isSelected()) {
            return "World1";
        } else if (world2RadioButton.isSelected()) {
            return "World2";
        } else if (world3RadioButton.isSelected()) {
            return "World3";
        } else if (world4RadioButton.isSelected()) {
            return "World4";
        } else if (world5RadioButton.isSelected()) {
            return "World5";
        } else if (serverRadioButton.isSelected()) {
            return "Server";
        } else {
            return null;
        }
    }

    // Set selected world on the Backup tab based on string passed in
    private void setSelectedWorld(String world) {
        if (world.compareTo("World1") == 0) {
            world1RadioButton.setSelected(true);
        } else if (world.compareTo("World2") == 0) {
            world2RadioButton.setSelected(true);
        } else if (world.compareTo("World3") == 0) {
            world3RadioButton.setSelected(true);
        } else if (world.compareTo("World4") == 0) {
            world4RadioButton.setSelected(true);
        } else if (world.compareTo("World5") == 0) {
            world5RadioButton.setSelected(true);
        } else if (world.compareTo("Server") == 0) {
            serverRadioButton.setSelected(true);
        }

    }

    // Game has 5 save slots, World1-World5, see if worldNum is valid
    private boolean validWorldNum(int worldNum) {
        return worldNum >= 1 && worldNum <= 5;
    }

    // Determine if Minecraft saves exist in directory representation of
    // parentDirStr
    private boolean savesExist(String parentDirStr) {
        String[] childDirStrs = new File(parentDirStr).list();

        if (childDirStrs.length == 0) {
            return false;
        }

        for (int i = 0; i < childDirStrs.length; i++) {
            if (childDirStrs[i].length() != 6) {
                continue;
            }

            boolean curIsDir = new File(parentDirStr, childDirStrs[i]).isDirectory();

            if (curIsDir && childDirStrs[i].startsWith("World")) {
                if (validWorldNum(Integer.parseInt(childDirStrs[i].charAt(5) + ""))) {
                    return true;
                }
            }
        }

        return false;
    }

    // Determine if backups generated by this program exist in directory
    // representation of parentDirStr
    private boolean backupsExist(String parentDirStr) {
        File file = new File(parentDirStr);

        if (!file.isDirectory()) {
            return false;
        }

        String[] childDirStrs = file.list();
        LinkedList<String> validBackups = new LinkedList<String>();

        // No sub-directories means no backups generated by this program
        if (childDirStrs.length == 0) {
            resetComboBoxAndInfo();
            return false;
        }

        // Go through each child in this directory and add any valid backups
        // to validBackups linkedlist
        for (int i = 0; i < childDirStrs.length; i++) {
            if (childDirStrs[i].length() != 30) {
                continue;
            }

            boolean curIsZip = isZip(new File(parentDirStr, childDirStrs[i]));

            if (curIsZip) {
                if (childDirStrs[i].startsWith("World")) {
                    if (validWorldNum(Integer.parseInt(childDirStrs[i].charAt(5) + ""))) {
                        validBackups.add(childDirStrs[i]);
                    }
                } else if (childDirStrs[i].startsWith("Server")) {
                    validBackups.add(childDirStrs[i]);
                }
            }
        }

        // No backups added to validBackups linkedlist means that directory
        // does not contain backups generated by this program
        if (validBackups.isEmpty()) {
            resetComboBoxAndInfo();
            return false;
        }

        String[] strValidBackups = new String[validBackups.size()];

        // Get string names of the backups found
        for (int i = 0; i < validBackups.size(); i++) {
            strValidBackups[i] = validBackups.get(i);
        }

        // Sort backup names from most recent to oldest
        Arrays.sort(strValidBackups, new Comparator<String>() {

            @Override
            public int compare(String s1, String s2) {
                String parsedS1 = parseName(s1);
                String parsedS2 = parseName(s2);

                return parsedS2.compareTo(parsedS1);
            }
        });

        // Set combo box to store the backup names
        backupsComboBox.setModel(new javax.swing.DefaultComboBoxModel(strValidBackups));
        backupsComboBox.setEnabled(true);
        backupsComboBox.setSelectedIndex(0);

        // Backups do exist at this point
        return true;
    }

    // Remove hyphens from zip name, does not include 'World#-'
    private String parseName(String s) {
        String year = s.substring(7, 11);
        String month = s.substring(12, 14);
        String day = s.substring(15, 17);
        String hour = s.substring(18, 20);
        String min = s.substring(21, 23);
        String sec = s.substring(24, 26);

        return year + month + day + hour + min + sec;
    }

    // Clear combo box and text fields next to it
    private void resetComboBoxAndInfo() {
        backupsComboBox.setModel(new javax.swing.DefaultComboBoxModel());
        backupsComboBox.setEnabled(false);

        worldText.setText("World Number");
        dateText.setText("MM/DD/YYYY");
        timeText.setText("HH:MM:SS AM/PM");
    }

    // Determine if file is a zip file
    private boolean isZip(File file) {
        return file.getName().endsWith(".zip");
    }

    // Returns formatted string based upon time of creation
    private String zipNamer(String name) {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        return name + "-" + df.format(new Date()) + ".zip";
    }

    // Determine if server is to be backed up
    private boolean serverBackup() {
        return worldToBackup != null && worldToBackup.compareTo("Server") == 0;
    }

    private boolean serverRestore() {
        return worldToRestore != null && worldToRestore.compareTo("Server") == 0;
    }

    // Determine if directory is server directory
    private boolean isServerDirectory(String parentDirStr) {
//        String[] childDirStrs = new File(parentDirStr).list();
        if (parentDirStr == null) {
            return false;
        }

        File file = new File(parentDirStr);

        if (!file.isDirectory()) {
            return false;
        }

        String[] childDirStrs = file.list();

        if (childDirStrs.length == 0) {
            return false;
        }

        for (int i = 0; i < childDirStrs.length; i++) {
            File curFile = new File(parentDirStr, childDirStrs[i]);

            // If it has a server.properties file, it's a server directory
            if (curFile.getName().compareTo("server.properties") == 0) {
                return true;
            }
        }

        // At this point, it is not a server directory
        return false;
    }

    private boolean backupButtonReady() {
        return saveDirSelectedB && backupDirSelectedB;
    }

    private boolean restoreButtonReady() {
        if (backupDir == null) {
            return false;
        }

        return backupsExist(backupDir.toString()) && backupDirSelectedR && saveDirSelectedR;
    }

    // Apply a particular setting
    private void applySetting(String name, String val) {
        if (name.compareTo(SAVE_DIRECTORY) == 0) {
            File saveDirCopy = saveDir;

            if (val.compareTo("null") == 0) {
                return;
            } else if (val.compareTo("null") != 0 && saveDir == null) {
                saveDir = new File(val);
            } else {
                if (saveDir != null && val.compareTo(saveDir.getAbsolutePath()) == 0) {
                    return;
                } else if (saveDir != null && val.compareTo(saveDir.getAbsolutePath()) != 0) {
                    saveDir = new File(val);
                }
            }

            if (saveDir.isDirectory()) {
                saveDirTextB.setForeground(Color.BLACK);
                saveDirTextB.setText(saveDir.toString());
                saveDirSelectedB = true;

                saveDirTextR.setForeground(Color.BLACK);
                saveDirTextR.setText(saveDir.toString());
                saveDirSelectedR = true;
            } else {
                saveDir = saveDirCopy;
            }
        } else if (name.compareTo(BACKUPS_DIRECTORY) == 0) {
            if (val.compareTo("null") == 0) {
                return;
            } else {
                backupDir = new File(val);
            }

            if (backupDir.isDirectory()) {
                backupDirTextB.setForeground(Color.BLACK);
                backupDirTextB.setText(backupDir.toString());
                backupDirSelectedB = true;

                backupDirTextR.setForeground(Color.BLACK);
                backupDirTextR.setText(backupDir.toString());
                backupDirSelectedR = true;
            } else {
                backupDir = null;
            }
        } else if (name.compareTo(LAST_BACKUP) == 0) {
            setSelectedWorld(val);
        }
        else if (name.compareTo(ASKED_ABOUT_SCRIPTS) == 0) {
            if (val != null && val.compareTo("true") == 0)
                askedAboutScripts = true;
            else
                askedAboutScripts = false;
        }
        else if (name.compareTo(USE_SCRIPTS) == 0) {
            if (val != null && val.compareTo("true") == 0)
                usingScripts = true;
            else
                usingScripts = false;
        }
        // TODO Add additional settings below this line if needed...
    }

    // Update a particular setting
    private String updateSetting(String name, String val) {
        if (name.compareTo(SAVE_DIRECTORY) == 0) {
            return name + SETTINGS_SEPARATOR + saveDir.getAbsolutePath();
        } else if (name.compareTo(BACKUPS_DIRECTORY) == 0) {
            return name + SETTINGS_SEPARATOR + backupDir.getAbsolutePath();
        } else if (name.compareTo(LAST_BACKUP) == 0) {
            if (worldToBackup == null) {
                return name + SETTINGS_SEPARATOR + val;
            } else {
                return name + SETTINGS_SEPARATOR + worldToBackup;
            }
        }
        else if (name.compareTo(ASKED_ABOUT_SCRIPTS) == 0) {
            return name + SETTINGS_SEPARATOR + askedAboutScripts;
        }
        else if (name.compareTo(USE_SCRIPTS) == 0) {
            return name + SETTINGS_SEPARATOR + usingScripts;
        }
        // TODO Add additional settings below this line if needed...
        else {
            return null;
        }
    }

    // Write all the settings from linkedlist of setting lines
    private void writeSettings(LinkedList<String> ll) {
        PrintWriter settingsWriter = null;

        try {
            settingsWriter = new PrintWriter(new File(SETTINGS_FILE));
            for (String s : ll) {
                settingsWriter.println(s);
            }
        } catch (FileNotFoundException ex) {
        } finally {
            if (settingsWriter != null) {
                settingsWriter.close();
            }
        }
    }

    // Write all the settings when no settings file exists
    private void writeSettings() {
        PrintWriter settingsWriter = null;

        try {
            settingsWriter = new PrintWriter(new File(SETTINGS_FILE));
            settingsWriter.println(SAVE_DIRECTORY + SETTINGS_SEPARATOR + null);
            settingsWriter.println(BACKUPS_DIRECTORY + SETTINGS_SEPARATOR + null);
            settingsWriter.println(LAST_BACKUP + SETTINGS_SEPARATOR + null);
            settingsWriter.println(ASKED_ABOUT_SCRIPTS + SETTINGS_SEPARATOR + null);
            settingsWriter.println(USE_SCRIPTS + SETTINGS_SEPARATOR + null);
            // TODO Add additional settings below this line if needed...
        } catch (FileNotFoundException ex) {
        } finally {
            if (settingsWriter != null) {
                settingsWriter.close();
            }
        }
    }

    // Update settings file so user has less to do on next use
    private void updateSettingsFile() {
        Scanner settingsScanner = null;
        LinkedList<String> settingsLines = null;

        try {
            settingsScanner = new Scanner(new File(SETTINGS_FILE));
            settingsLines = new LinkedList<String>();

            while (settingsScanner.hasNextLine()) {
                Scanner curLine = new Scanner(settingsScanner.nextLine()).useDelimiter("=");

                String settingName = curLine.next();
                String settingVal = curLine.next();

                String newCurLine = updateSetting(settingName, settingVal);

                if (newCurLine != null) {
                    settingsLines.add(newCurLine);
                }
            }
            settingsScanner.close();

            writeSettings(settingsLines);
        } catch (FileNotFoundException ex) {
            writeSettings();
        } finally {
            if (settingsScanner != null) {
                settingsScanner.close();
            }
        }
    }

    @SuppressWarnings("unchecked")
    // Initialize all GUI components
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        fileChooser = new javax.swing.JFileChooser();
        backupWorldsButtonGroup = new javax.swing.ButtonGroup();
        tabbedPane = new javax.swing.JTabbedPane();
        backupPanel = new javax.swing.JPanel();
        saveDirLabel = new javax.swing.JLabel();
        saveDirTextB = new javax.swing.JTextField();
        saveDirBrowseButtonB = new javax.swing.JButton();
        selectWorldsLabel = new javax.swing.JLabel();
        world1RadioButton = new javax.swing.JRadioButton();
        world2RadioButton = new javax.swing.JRadioButton();
        world3RadioButton = new javax.swing.JRadioButton();
        world4RadioButton = new javax.swing.JRadioButton();
        world5RadioButton = new javax.swing.JRadioButton();
        backupDirLabelB = new javax.swing.JLabel();
        backupDirTextB = new javax.swing.JTextField();
        backupDirBrowseButtonB = new javax.swing.JButton();
        backupButton = new javax.swing.JButton();
        backupStatusLabel = new javax.swing.JLabel();
        statusScrollPane = new javax.swing.JScrollPane();
        backupStatusTextArea = new javax.swing.JTextArea();
        selectWorldsStatusLabel = new javax.swing.JLabel();
        serverRadioButton = new javax.swing.JRadioButton();
        restorePanel = new javax.swing.JPanel();
        backupDirLabelR = new javax.swing.JLabel();
        backupDirTextR = new javax.swing.JTextField();
        backupDirBrowseButtonR = new javax.swing.JButton();
        saveDirLabelR = new javax.swing.JLabel();
        saveDirTextR = new javax.swing.JTextField();
        saveDirBrowseButtonR = new javax.swing.JButton();
        listOfBackupsLabel = new javax.swing.JLabel();
        backupsComboBox = new javax.swing.JComboBox();
        worldLabel = new javax.swing.JLabel();
        dateLabel = new javax.swing.JLabel();
        timeLabel = new javax.swing.JLabel();
        worldText = new javax.swing.JTextField();
        dateText = new javax.swing.JTextField();
        timeText = new javax.swing.JTextField();
        restoreButton = new javax.swing.JButton();
        restoreStatusLabel = new javax.swing.JLabel();
        statusScrollPane1 = new javax.swing.JScrollPane();
        restoreStatusTextArea = new javax.swing.JTextArea();
        mcsMenuBar = new javax.swing.JMenuBar();
        fileMenu = new javax.swing.JMenu();
        closeMenuItem = new javax.swing.JMenuItem();
        aboutMenu = new javax.swing.JMenu();
        aboutMCSMenuItem = new javax.swing.JMenuItem();
        licenseMenuItem = new javax.swing.JMenuItem();
        helpMenuItem = new javax.swing.JMenu();
        documentationMenuitem = new javax.swing.JMenuItem();
        feedbackMenu = new javax.swing.JMenu();
        feedbackMenuItem = new javax.swing.JMenuItem();

        fileChooser.setCurrentDirectory(new java.io.File("C:\\Program Files\\NetBeans 6.9.1\\null"));
        fileChooser.setFileSelectionMode(javax.swing.JFileChooser.DIRECTORIES_ONLY);

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Minecraft Savior  --  Created by JS");

        saveDirLabel.setText("Minecraft Save Directory (or server directory):");

        saveDirTextB.setEditable(false);
        saveDirTextB.setText("Browse to Minecraft's local 'saves' directory on your machine...");
        saveDirTextB.setToolTipText("For Windows users, this is \"C:\\Users\\<Username>\\AppData\\Roaming\\.minecraft\\saves\".");

        saveDirBrowseButtonB.setText("...");
        saveDirBrowseButtonB.setToolTipText("Click to browse your local file system...");
        saveDirBrowseButtonB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveDirBrowseButtonBActionPerformed(evt);
            }
        });

        selectWorldsLabel.setText("Select the world you would like to backup:");

        backupWorldsButtonGroup.add(world1RadioButton);
        world1RadioButton.setText("World1");

        backupWorldsButtonGroup.add(world2RadioButton);
        world2RadioButton.setText("World2");

        backupWorldsButtonGroup.add(world3RadioButton);
        world3RadioButton.setText("World3");

        backupWorldsButtonGroup.add(world4RadioButton);
        world4RadioButton.setText("World4");

        backupWorldsButtonGroup.add(world5RadioButton);
        world5RadioButton.setText("World5");

        backupDirLabelB.setText("Backup Directory:");

        backupDirTextB.setEditable(false);
        backupDirTextB.setText("Browse to a safe location where you would like to make the backup...");
        backupDirTextB.setToolTipText("It might be a good idea to create a directory for backups so you know exactly where all of your backups are located.");

        backupDirBrowseButtonB.setText("...");
        backupDirBrowseButtonB.setToolTipText("Click to browse your local file system...");
        backupDirBrowseButtonB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                backupDirBrowseButtonBActionPerformed(evt);
            }
        });

        backupButton.setText("Perform backup!");
        backupButton.setEnabled(false);
        backupButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                backupButtonActionPerformed(evt);
            }
        });

        backupStatusLabel.setText("Backup Status:");

        backupStatusTextArea.setColumns(20);
        backupStatusTextArea.setEditable(false);
        backupStatusTextArea.setRows(5);
        backupStatusTextArea.setTabSize(4);
        backupStatusTextArea.setToolTipText("When you press the \"Perform Backup!\" button, this area will display detailed information on the files currently being compressed.");
        statusScrollPane.setViewportView(backupStatusTextArea);

        selectWorldsStatusLabel.setForeground(new java.awt.Color(255, 0, 0));

        backupWorldsButtonGroup.add(serverRadioButton);
        serverRadioButton.setText("Server");

        javax.swing.GroupLayout backupPanelLayout = new javax.swing.GroupLayout(backupPanel);
        backupPanel.setLayout(backupPanelLayout);
        backupPanelLayout.setHorizontalGroup(
            backupPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(backupPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(backupPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(statusScrollPane, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 602, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, backupPanelLayout.createSequentialGroup()
                        .addComponent(saveDirTextB, javax.swing.GroupLayout.PREFERRED_SIZE, 573, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(saveDirBrowseButtonB, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(saveDirLabel, javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, backupPanelLayout.createSequentialGroup()
                        .addComponent(selectWorldsLabel)
                        .addGap(18, 18, 18)
                        .addComponent(selectWorldsStatusLabel))
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, backupPanelLayout.createSequentialGroup()
                        .addComponent(world1RadioButton)
                        .addGap(18, 18, 18)
                        .addComponent(world2RadioButton)
                        .addGap(18, 18, 18)
                        .addComponent(world3RadioButton)
                        .addGap(18, 18, 18)
                        .addComponent(world4RadioButton)
                        .addGap(18, 18, 18)
                        .addComponent(world5RadioButton)
                        .addGap(18, 18, 18)
                        .addComponent(serverRadioButton))
                    .addComponent(backupDirLabelB, javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, backupPanelLayout.createSequentialGroup()
                        .addComponent(backupDirTextB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(backupDirBrowseButtonB, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(backupButton, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 175, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(backupStatusLabel, javax.swing.GroupLayout.Alignment.LEADING))
                .addContainerGap())
        );

        backupPanelLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {backupDirTextB, saveDirTextB});

        backupPanelLayout.setVerticalGroup(
            backupPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(backupPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(saveDirLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(backupPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(saveDirTextB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(saveDirBrowseButtonB))
                .addGap(18, 18, 18)
                .addGroup(backupPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(selectWorldsLabel)
                    .addComponent(selectWorldsStatusLabel))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(backupPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(world1RadioButton)
                    .addComponent(world2RadioButton)
                    .addComponent(world3RadioButton)
                    .addComponent(world4RadioButton)
                    .addComponent(world5RadioButton)
                    .addComponent(serverRadioButton))
                .addGap(18, 18, 18)
                .addComponent(backupDirLabelB)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(backupPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(backupDirTextB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(backupDirBrowseButtonB))
                .addGap(18, 18, 18)
                .addComponent(backupButton)
                .addGap(13, 13, 13)
                .addComponent(backupStatusLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(statusScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 98, Short.MAX_VALUE)
                .addContainerGap())
        );

        tabbedPane.addTab("Backup", backupPanel);

        backupDirLabelR.setText("Backup Directory:");

        backupDirTextR.setEditable(false);
        backupDirTextR.setText("Browse to the location where your backups are stored...");
        backupDirTextR.setToolTipText("It might be a good idea to create a directory for backups so you know exactly where all of your backups are located.");

        backupDirBrowseButtonR.setText("...");
        backupDirBrowseButtonR.setToolTipText("Click to browse your local file system...");
        backupDirBrowseButtonR.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                backupDirBrowseButtonRActionPerformed(evt);
            }
        });

        saveDirLabelR.setText("Minecraft Save Directory (or server directory):");

        saveDirTextR.setEditable(false);
        saveDirTextR.setText("Browse to Minecraft's local 'saves' directory on your machine...");
        saveDirTextR.setToolTipText("For Windows users, this is \"C:\\Users\\<Username>\\AppData\\Roaming\\.minecraft\\saves\".");

        saveDirBrowseButtonR.setText("...");
        saveDirBrowseButtonR.setToolTipText("Click to browse your local file system...");
        saveDirBrowseButtonR.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveDirBrowseButtonRActionPerformed(evt);
            }
        });

        listOfBackupsLabel.setText("List of Backups:");

        backupsComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        backupsComboBox.setToolTipText("Select the particular backup you would like to restore to.");
        backupsComboBox.setEnabled(false);
        backupsComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                backupsComboBoxActionPerformed(evt);
            }
        });

        worldLabel.setText("World:");

        dateLabel.setText("Date:");

        timeLabel.setText("Time:");

        worldText.setEditable(false);
        worldText.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
        worldText.setText("World Number");

        dateText.setEditable(false);
        dateText.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
        dateText.setText("MM/DD/YYYY");

        timeText.setEditable(false);
        timeText.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
        timeText.setText("HH:MM:SS AM/PM");

        restoreButton.setText("Perform Restore!");
        restoreButton.setEnabled(false);
        restoreButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                restoreButtonActionPerformed(evt);
            }
        });

        restoreStatusLabel.setText("Restore Status:");

        restoreStatusTextArea.setColumns(20);
        restoreStatusTextArea.setEditable(false);
        restoreStatusTextArea.setRows(5);
        restoreStatusTextArea.setTabSize(4);
        restoreStatusTextArea.setToolTipText("When you press the \"Perform Restore!\" button, this area will display detailed information on the files currently being decompressed.");
        statusScrollPane1.setViewportView(restoreStatusTextArea);

        javax.swing.GroupLayout restorePanelLayout = new javax.swing.GroupLayout(restorePanel);
        restorePanel.setLayout(restorePanelLayout);
        restorePanelLayout.setHorizontalGroup(
            restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, restorePanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(statusScrollPane1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 602, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, restorePanelLayout.createSequentialGroup()
                        .addComponent(backupDirTextR, javax.swing.GroupLayout.PREFERRED_SIZE, 573, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(backupDirBrowseButtonR, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(backupDirLabelR, javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(saveDirLabelR, javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, restorePanelLayout.createSequentialGroup()
                        .addComponent(saveDirTextR, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(saveDirBrowseButtonR, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(listOfBackupsLabel, javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, restorePanelLayout.createSequentialGroup()
                        .addComponent(backupsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 234, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addGroup(restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(timeLabel)
                            .addComponent(dateLabel)
                            .addComponent(worldLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(dateText, javax.swing.GroupLayout.DEFAULT_SIZE, 78, Short.MAX_VALUE)
                            .addComponent(timeText)
                            .addComponent(worldText, javax.swing.GroupLayout.PREFERRED_SIZE, 105, javax.swing.GroupLayout.PREFERRED_SIZE)))
                    .addComponent(restoreStatusLabel, javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(restoreButton, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 174, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );

        restorePanelLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {backupDirTextR, saveDirTextR});

        restorePanelLayout.setVerticalGroup(
            restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(restorePanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(backupDirLabelR)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(backupDirTextR, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(backupDirBrowseButtonR))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(saveDirLabelR)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(saveDirTextR, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(saveDirBrowseButtonR))
                .addGroup(restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(restorePanelLayout.createSequentialGroup()
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(listOfBackupsLabel)
                        .addGap(9, 9, 9)
                        .addComponent(backupsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(restorePanelLayout.createSequentialGroup()
                        .addGap(23, 23, 23)
                        .addGroup(restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(worldLabel)
                            .addComponent(worldText, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(dateText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(dateLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(restorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(timeText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(timeLabel))))
                .addGap(2, 2, 2)
                .addComponent(restoreButton)
                .addGap(10, 10, 10)
                .addComponent(restoreStatusLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(statusScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 98, Short.MAX_VALUE)
                .addContainerGap())
        );

        tabbedPane.addTab("Restore", restorePanel);

        fileMenu.setText("File");

        closeMenuItem.setText("Close MCS");
        closeMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                closeMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(closeMenuItem);

        mcsMenuBar.add(fileMenu);

        aboutMenu.setText("About");

        aboutMCSMenuItem.setText("MCS");
        aboutMCSMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                aboutMCSMenuItemActionPerformed(evt);
            }
        });
        aboutMenu.add(aboutMCSMenuItem);

        licenseMenuItem.setText("GPL License");
        licenseMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                licenseMenuItemActionPerformed(evt);
            }
        });
        aboutMenu.add(licenseMenuItem);

        mcsMenuBar.add(aboutMenu);

        helpMenuItem.setText("Help");

        documentationMenuitem.setText("MCS Documentation");
        documentationMenuitem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                documentationMenuitemActionPerformed(evt);
            }
        });
        helpMenuItem.add(documentationMenuitem);

        mcsMenuBar.add(helpMenuItem);

        feedbackMenu.setText("Feedback");

        feedbackMenuItem.setText("Your Feedback Helps!");
        feedbackMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                feedbackMenuItemActionPerformed(evt);
            }
        });
        feedbackMenu.add(feedbackMenuItem);

        mcsMenuBar.add(feedbackMenu);

        setJMenuBar(mcsMenuBar);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(tabbedPane, javax.swing.GroupLayout.DEFAULT_SIZE, 627, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(tabbedPane, javax.swing.GroupLayout.DEFAULT_SIZE, 388, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    // Backup tab - handles user choosing their local Minecraft 'saves' directory
    private void saveDirBrowseButtonBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveDirBrowseButtonBActionPerformed
        int returnVal = fileChooser.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            saveDir = fileChooser.getSelectedFile();

            // For single-player 'saves' directory (if auto-find does not work)
            if (saveDir.getName().compareTo("saves") == 0) {
                if (!savesExist(saveDir.getAbsolutePath())) {
                    saveDirTextB.setForeground(Color.RED);
                    saveDirTextB.setText("No saves found in Minecraft directory.");
                    return;
                }

                saveDirTextB.setForeground(Color.BLACK);
                saveDirTextB.setText(saveDir.toString());
                saveDirSelectedB = true;
            } // For server directory
            else if (isServerDirectory(saveDir.getAbsolutePath())) {
                saveDirTextB.setForeground(Color.BLACK);
                saveDirTextB.setText(saveDir.toString());
                saveDirSelectedB = true;

                serverRadioButton.setSelected(true);
            } else {
                saveDirTextB.setForeground(Color.RED);
                saveDirTextB.setText("Incorrect choice. Select the 'saves' folder "
                        + "in your Minecraft folder, or your whole server folder!");
                saveDirSelectedB = false;

                if (backupButton.isEnabled()) {
                    backupButton.setEnabled(false);
                }
            }

            if (backupButtonReady()) {
                backupButton.setEnabled(true);
            }
        }

        fileChooser.setCurrentDirectory(null);
    }//GEN-LAST:event_saveDirBrowseButtonBActionPerformed

    // Backup tab - handles user choosing their backups directory
    private void backupDirBrowseButtonBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backupDirBrowseButtonBActionPerformed
        int returnVal = fileChooser.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            backupDir = fileChooser.getSelectedFile();
            backupDirTextB.setForeground(Color.BLACK);
            backupDirTextB.setText(backupDir.toString());
            backupDirSelectedB = true;

            if (backupButtonReady()) {
                backupButton.setEnabled(true);
            }
        }

        fileChooser.setCurrentDirectory(null);
}//GEN-LAST:event_backupDirBrowseButtonBActionPerformed

    // Backups tab - handles user clicking the backupButton
    private void backupButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backupButtonActionPerformed
        try {
            backupStatusTextArea.setText("");

            worldToBackup = getSelectedWorld();

            if (worldToBackup == null) {
                selectWorldsStatusLabel.setText("Select a world first!");
                return;
            }
            if (serverBackup() && !isServerDirectory(saveDir.toString())) {
                selectWorldsStatusLabel.setText("Directory above is not a server directory!");
                saveDirSelectedB = false;
                return;
            }

            if (!serverBackup() && !(new File(saveDir.toString() + File.separator + worldToBackup).isDirectory())) {
                selectWorldsStatusLabel.setText("No save files found for "
                        + worldToBackup + "!");
                return;
            } else {
                selectWorldsStatusLabel.setText("");
            }

            backupButton.setEnabled(false);

            backupTask = new BackupTask();
            backupTask.execute();
        } catch (Exception e) {
        }
    }//GEN-LAST:event_backupButtonActionPerformed

    // Restore tab - handles user choosing their backups directory
    private void backupDirBrowseButtonRActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backupDirBrowseButtonRActionPerformed
        int returnVal = fileChooser.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            backupDir = fileChooser.getSelectedFile();
            backupDirTextR.setForeground(Color.BLACK);
            backupDirTextR.setText(backupDir.toString());
            backupDirSelectedR = true;

            if (restoreButtonReady()) {
                restoreButton.setEnabled(true);
            }
        }

        fileChooser.setCurrentDirectory(null);
    }//GEN-LAST:event_backupDirBrowseButtonRActionPerformed

    // Restore tab - handles user choosing their local Minecraft 'saves' directory
    private void saveDirBrowseButtonRActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveDirBrowseButtonRActionPerformed
        int returnVal = fileChooser.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            saveDir = fileChooser.getSelectedFile();

            // For single-player 'saves' directory (if auto-find does not work)
            // or multiplayer server directory
            if (saveDir.getName().compareTo("saves") == 0 || isServerDirectory(saveDir.getAbsolutePath())) {
                saveDirTextR.setForeground(Color.BLACK);
                saveDirTextR.setText(saveDir.toString());
                saveDirSelectedR = true;

                if (restoreButtonReady()) {
                    backupsComboBox.setEnabled(true);
                    restoreButton.setEnabled(true);
                }
            } else {
                saveDirTextR.setForeground(Color.RED);
                saveDirTextR.setText("Incorrect choice. Select the 'saves' folder "
                        + "in your Minecraft folder, or your whole server folder!");
                saveDirSelectedR = false;

                backupsComboBox.setEnabled(false);

                if (restoreButton.isEnabled()) {
                    restoreButton.setEnabled(false);
                }
            }
        }

        fileChooser.setCurrentDirectory(null);
    }//GEN-LAST:event_saveDirBrowseButtonRActionPerformed

    // Restore tab - handles user selecting a backup from combo box
    private void backupsComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backupsComboBoxActionPerformed
        javax.swing.JComboBox jcb = (javax.swing.JComboBox) evt.getSource();
        String zipName = (String) jcb.getSelectedItem();

        restoreZip = zipName;
        worldToRestore = zipName.substring(0, 6);

        String year = zipName.substring(7, 11);
        String month = zipName.substring(12, 14);
        String day = zipName.substring(15, 17);

        String hour = zipName.substring(18, 20);
        int hourNum = Integer.parseInt(hour);

        String min = zipName.substring(21, 23);
        String sec = zipName.substring(24, 26);

        if (!serverRestore()) {
            worldText.setText(zipName.charAt(5) + "");
        } else {
            worldText.setText("Server");
        }

        dateText.setText(month + "/" + day + "/" + year);

        if (hourNum <= 12) {
            if (hourNum == 0) {
                timeText.setText("12:" + min + ":" + sec + " AM");
            } else {
                timeText.setText(hour + ":" + min + ":" + sec + " AM");
            }
        } else {
            int newHour = hourNum - 12;
            timeText.setText(newHour + ":" + min + ":" + sec + " PM");
        }
    }//GEN-LAST:event_backupsComboBoxActionPerformed

    // Restore tab - handles user clicking restoreButton
    private void restoreButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_restoreButtonActionPerformed
        restoreStatusTextArea.setText("");
        restoreButton.setEnabled(false);

        if (worldToRestore.startsWith("World") && saveDir.getName().compareTo("saves") != 0) {
            saveDirTextR.setForeground(Color.RED);
            saveDirTextR.setText("Incorrect choice for world restore. Select the 'saves' "
                    + "folder in your local Minecraft folder!");
            saveDirSelectedR = false;

            backupsComboBox.setEnabled(false);
            return;
        }

        restoreTask = new RestoreTask();
        restoreTask.execute();
    }//GEN-LAST:event_restoreButtonActionPerformed

    private void closeMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeMenuItemActionPerformed
        System.exit(0);
    }//GEN-LAST:event_closeMenuItemActionPerformed

    private void licenseMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_licenseMenuItemActionPerformed
        new License(this, true).setVisible(true);
    }//GEN-LAST:event_licenseMenuItemActionPerformed

    private void aboutMCSMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aboutMCSMenuItemActionPerformed
        new Info(this, true).setVisible(true);
    }//GEN-LAST:event_aboutMCSMenuItemActionPerformed

    private void documentationMenuitemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentationMenuitemActionPerformed
        new Documentation(this, true).setVisible(true);
    }//GEN-LAST:event_documentationMenuitemActionPerformed

    private void feedbackMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_feedbackMenuItemActionPerformed
        new Feedback(this, true).setVisible(true);
    }//GEN-LAST:event_feedbackMenuItemActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenuItem aboutMCSMenuItem;
    private javax.swing.JMenu aboutMenu;
    private javax.swing.JButton backupButton;
    private javax.swing.JButton backupDirBrowseButtonB;
    private javax.swing.JButton backupDirBrowseButtonR;
    private javax.swing.JLabel backupDirLabelB;
    private javax.swing.JLabel backupDirLabelR;
    private javax.swing.JTextField backupDirTextB;
    private javax.swing.JTextField backupDirTextR;
    private javax.swing.JPanel backupPanel;
    private javax.swing.JLabel backupStatusLabel;
    private static javax.swing.JTextArea backupStatusTextArea;
    private javax.swing.ButtonGroup backupWorldsButtonGroup;
    private javax.swing.JComboBox backupsComboBox;
    private javax.swing.JMenuItem closeMenuItem;
    private javax.swing.JLabel dateLabel;
    private javax.swing.JTextField dateText;
    private javax.swing.JMenuItem documentationMenuitem;
    private javax.swing.JMenu feedbackMenu;
    private javax.swing.JMenuItem feedbackMenuItem;
    private javax.swing.JFileChooser fileChooser;
    private javax.swing.JMenu fileMenu;
    private javax.swing.JMenu helpMenuItem;
    private javax.swing.JMenuItem licenseMenuItem;
    private javax.swing.JLabel listOfBackupsLabel;
    private javax.swing.JMenuBar mcsMenuBar;
    private javax.swing.JButton restoreButton;
    private javax.swing.JPanel restorePanel;
    private javax.swing.JLabel restoreStatusLabel;
    private static javax.swing.JTextArea restoreStatusTextArea;
    private javax.swing.JButton saveDirBrowseButtonB;
    private javax.swing.JButton saveDirBrowseButtonR;
    private javax.swing.JLabel saveDirLabel;
    private javax.swing.JLabel saveDirLabelR;
    private javax.swing.JTextField saveDirTextB;
    private javax.swing.JTextField saveDirTextR;
    private javax.swing.JLabel selectWorldsLabel;
    private javax.swing.JLabel selectWorldsStatusLabel;
    private javax.swing.JRadioButton serverRadioButton;
    private javax.swing.JScrollPane statusScrollPane;
    private javax.swing.JScrollPane statusScrollPane1;
    private javax.swing.JTabbedPane tabbedPane;
    private javax.swing.JLabel timeLabel;
    private javax.swing.JTextField timeText;
    private javax.swing.JRadioButton world1RadioButton;
    private javax.swing.JRadioButton world2RadioButton;
    private javax.swing.JRadioButton world3RadioButton;
    private javax.swing.JRadioButton world4RadioButton;
    private javax.swing.JRadioButton world5RadioButton;
    private javax.swing.JLabel worldLabel;
    private javax.swing.JTextField worldText;
    // End of variables declaration//GEN-END:variables

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new MCS().setVisible(true);
            }
        });
    }

    // An instance of BackupTask does the backup operation
    class BackupTask extends SwingWorker<Void, Void> {
        // Main task. Executed in background thread.

        @Override
        public Void doInBackground() {
            String zipName = zipNamer(worldToBackup);
            backupStatusTextArea.append("Creating zip archive '" + zipName + "' in "
                    + backupDir.toString() + "...\n");

            try {
                if (!serverBackup()) {
                    Compress.compressDir(saveDir.toString() + File.separator + worldToBackup,
                            backupDir.toString() + File.separator + zipName);
                } else {
                    Compress.compressDir(saveDir.toString(), backupDir.toString() + File.separator + zipName);
                }
            } catch (IOException ex) {
            }

            return null;
        }

        // Executed in event dispatching thread
        @Override
        public void done() {
            backupsExist(backupDir.toString());
            updateSettingsFile();
            Toolkit.getDefaultToolkit().beep();
            backupButton.setEnabled(true);
            backupStatusTextArea.append("Done!");
        }
    }

    // An instance of RestoreTask does the restore operation
    class RestoreTask extends SwingWorker<Void, Void> {
        // Main task. Executed in background thread.

        @Override
        public Void doInBackground() {
            String zipName = restoreZip;
            restoreStatusTextArea.append("Decompressing '" + zipName + "' to "
                    + saveDir.toString() + "...\n");

            if (!serverRestore()) {
                Decompress.decompressDir(backupDir.toString() + File.separator + restoreZip,
                        saveDir.toString() + File.separator + worldToRestore);
            } else {
                Decompress.decompressDir(backupDir.toString() + File.separator + restoreZip,
                        saveDir.toString());
            }

            return null;
        }

        // Executed in event dispatching thread
        @Override
        public void done() {
            updateSettingsFile();
            Toolkit.getDefaultToolkit().beep();
            restoreButton.setEnabled(true);
            restoreStatusTextArea.append("Done!");
        }
    }
}
